package com.qf.springboot.config;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qf.springboot.permission.service.PersService;
import com.qf.springboot.permission.service.RolePermsService;
import com.qf.springboot.permission.service.RoleService;
import com.qf.springboot.permission.service.UserRoleService;
import com.qf.springboot.user.entity.User;
import com.qf.springboot.user.service.UserService;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;

//CustomRealm 有自定义的意思
//AuthorizingRealm是抽象类,里面既包含身份信息,又包含权限信息,所以要继承这个 Authorizing 授权的意思
public class CustomRealm extends AuthorizingRealm {
    //shiro处理登录流程时就会调用下面两个重写方法 doGetAuthenticationInfo 和 doGetAuthorizationInfo

    @Autowired
    UserService userService;

    //Realm给shiro提供必要的信息的,比如登录功能需要的相应身份信息和权限信息 Realm中文有领域 国土的意思
    /**
     * AuthorizingRealm抽象类的重写方法之一,通过这个方法,向shiro提供 当前用户的身份认证信息
     */
    @Override
    //doGetAuthenticationInfo 通过这个方法,向shiro提供 当前用户的 身份认证信息
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {

        System.out.println(token);

        //获取用户名 token.getPrincipal()  getPrincipal身份信息的意思
        String username = (String) token.getPrincipal();

        //根据token中的用户提供的身份信息(用户名),去数据库中查询用户的密码
        User user = getUserFromDB(username);

        //如果有异常的话,mvc框架会把当前请求 forward到 /error url中
        //user = null;
        if (user == null){
            throw new UnknownAccountException("用户不存在");
        }

        //需要把数据库中的用户名的密码返回给shiro
        //注意:第一个参数,可以放入从数据库中查询出来的完整的user对象,也可以只放入用户名(user.getName),
        //这里会影响后续从shiro中获得用户身份信息时获取到的对象,如果放入user对象,后续获得的身份信息就是user对象
        //第二个参数,放入数据库中查询出来的用户的密码 getPassword()
        //第三个参数,是固定写法,是当前提供身份信息的Realm对象的名字 this.getName()
        //SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());

        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(),
                ByteSource.Util.bytes(user.getSalt()),this.getName()); //把当前用户的salt提供给shiro

        return simpleAuthenticationInfo;
    }

    /**
     * 查询数据库,获得user对象
     * */
    private User getUserFromDB(String username) {

        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("username",username);

        User user = userService.getOne(userQueryWrapper);

        return  user;
    }

    /**
     * AuthorizingRealm抽象类的重写方法之二,通过这个方法,向Shiro提供当前用户的 权限相关信息
     */
    @Override
    //doGetAuthorizationInfo 通过这个方法,向shiro提供 当前用户的 权限相关信息 Principal英文是身份的意思
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        //获取当前用户的身份信息 user对象的信息
        User user = (User) principals.getPrimaryPrincipal();

        //去数据库中查询user对应的 角色 自定义设置两个方法,分别设置角色和权限
        List<String> roles = user.getRole();
        List<String> perms = user.getPerms();

        //角色和权限信息封装到 AuthorizationInfo 对象中
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

        //添加角色信息
        //simpleAuthorizationInfo.addRole("admin");
        simpleAuthorizationInfo.addRoles(roles);

        //添加权限信息 权限字符串
        //simpleAuthorizationInfo.addStringPermission("order:get");
        simpleAuthorizationInfo.addStringPermissions(perms);

        return simpleAuthorizationInfo;
    }
}
